from math import tanh
from smysql import DB

def dtanh(y):
    return 1.0-y*y

class searchnet(DB):
    def getstrength(self, fromid, toid, layer):
        if layer == 0:
            table = 'wordhidden'
        else:
            table = 'hiddenurl'
        res = self.execute("select strength from %s where fromid=%d and toid=%d" % (table, fromid, toid))
        if res is None:
            if layer == 0:
                return -0.2
            if layer == 1:
                return 0
        return res[0]

    def setstrength(self, fromid, toid, layer, strength):
        if layer == 0:
            table = 'wordhidden'
        else:
            table = 'hiddenurl'
        res = self.execute("select id from %s where fromid=%d and toid=%d" %
                           (table, fromid, toid))
        if res is None:
            self.execute("insert into %s (fromid,toid,strength) values(%d,%d,%f)" %
                         (table, fromid, toid, strength))
        else:
            rowid = res[0]
            self.execute("update %s set strength=%f where id=%d" % (table, strength, rowid))

    def generatehiddennide(self, wordids, urls):
        if len(wordids) > 3:
            return None
        createkey = '_'.join(sorted([str(wi) for wi in wordids]))
        print(createkey)
        res = self.execute("select id from hiddennode where create_key='%s'" % createkey)
        if res is None:
            hiddenid = self.execute("insert into hiddennode(create_key) value ('%s')" % createkey, last=True)

            for wordid in wordids:
                self.setstrength(wordid, hiddenid, 0, 1.0 / len(wordids))
            for urlid in urls:
                self.setstrength(hiddenid, urlid, 1, 0.1)
            self.commit()

    def getallhiddenids(self, wordids, urlids):
        l1 = {}
        for wordid in wordids:
            cur = self.execute("select toid from wordhidden where fromid=%d" % wordid,True)
            for row in cur:
                l1[row[0]] = 1
        for urlid in urlids:
            cur = self.execute("select fromid from hiddenurl where toid=%d" % urlid,True)
            for row in cur:
                l1[row[0]] = 1
        return list(l1.keys())

    def setupnetwork(self, wordids, urlids):
        self.wordids = wordids
        self.hiddenids = self.getallhiddenids(wordids, urlids)
        self.urlids = urlids

        self.ai = [1.0] * len(self.wordids)
        self.ah = [1.0] * len(self.hiddenids)
        self.ao = [1.0] * len(self.urlids)

        self.wi = [[self.getstrength(wordid, hiddenid, 0)
                    for hiddenid in self.hiddenids]
                   for wordid in self.wordids]
        self.wo = [[self.getstrength(hiddenid, urlid, 1)
                    for urlid in self.urlids]
                   for hiddenid in self.hiddenids]

    def feedforward(self):
        for i in range(len(self.wordids)):
            self.ai[i] = 1.0
        for j in range(len(self.hiddenids)):
            sum = 0.0
            for i in range(len(self.wordids)):
                sum += self.ai[i] * self.wi[i][j]
            self.ah[j] = tanh(sum)
        for k in range(len(self.urlids)):
            sum = 0.0
            for j in range(len(self.hiddenids)):
                sum += self.ah[j] * self.wo[j][k]
            self.ao[k] = tanh(sum)
        return self.ao[:]

    def getresult(self,wordids,urlids):
        self.setupnetwork(wordids,urlids)
        return self.feedforward()

    def backPropagate(self,targets,N=0.5):
        output_deltas=[0.0]*len(self.urlids)
        for k in range(len(self.urlids)):
            error=targets[k]-self.ao[k]
            output_deltas[k]=dtanh(self.ao[k])*error

        hidden_deltas=[0.0]*len(self.hiddenids)
        for j in range(len(self.hiddenids)):
            error=0.0
            for k in range(len(self.urlids)):
                error+=output_deltas[k]*self.wo[j][k]
            hidden_deltas[j]=dtanh(self.ah[j])*error

        for j in range(len(self.hiddenids)):
            for k in range(len(self.urlids)):
                change=output_deltas[k]*self.ah[j]
                self.wo[j][k]+=N*change

        for i in range(len(self.wordids)):
            for j in range(len(self.hiddenids)):
                change=hidden_deltas[j]*self.ai[i]
                self.wi[i][j]+=N*change

    def trainquery(self,worids,urlids,selectedurl):
        self.generatehiddennide(worids,urlids)
        self.setupnetwork(worids,urlids)
        self.feedforward()
        targets=[0.0]*len(urlids)
        targets[urlids.index(selectedurl)]=1.0
        self.backPropagate(targets)
        self.updatedatabase()


    def updatedatabase(self):
        for i in range(len(self.wordids)):
            for j in range(len(self.hiddenids)):
                self.setstrength(self.wordids[i],self.hiddenids[j],0,self.wi[i][j])
        for j in range(len(self.hiddenids)):
            for k in range(len(self.urlids)):
                self.setstrength(self.hiddenids[j],self.urlids[k],1,self.wo[j][k])
        self.commit()





